﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Windows.Media.Media3D10
{
    /// <summary>
    /// Met à l'échelle un objet dans le plan x-y-z tridimensionnel, à partir d'un point central défini.
    /// Les facteurs d'échelle sont définis sur les axes x -, y -, et z – à partir de ce point central.
    /// </summary>
    public sealed class ScaleTransform3D10 : AffineTransform3D10
    {
        #region Dependency Properties
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="CenterX"/>.
        /// </summary>
        public static readonly DependencyProperty CenterXProperty =
            DependencyProperty.Register("CenterX", typeof(float), typeof(ScaleTransform3D10),
            new PropertyMetadata(0.0f, new PropertyChangedCallback(CenterX_PropertyChanged)));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="CenterY"/>.
        /// </summary>
        public static readonly DependencyProperty CenterYProperty =
            DependencyProperty.Register("CenterY", typeof(float), typeof(ScaleTransform3D10),
            new PropertyMetadata(0.0f, new PropertyChangedCallback(CenterY_PropertyChanged)));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="CenterZ"/>.
        /// </summary>
        public static readonly DependencyProperty CenterZProperty =
            DependencyProperty.Register("CenterZ", typeof(float), typeof(ScaleTransform3D10),
            new PropertyMetadata(0.0f, new PropertyChangedCallback(CenterZ_PropertyChanged)));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="ScaleX"/>.
        /// </summary>
        public static readonly DependencyProperty ScaleXProperty =
            DependencyProperty.Register("ScaleX", typeof(float), typeof(ScaleTransform3D10),
            new PropertyMetadata(0.0f, new PropertyChangedCallback(ScaleX_PropertyChanged)));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="ScaleY"/>.
        /// </summary>
        public static readonly DependencyProperty ScaleYProperty =
            DependencyProperty.Register("ScaleY", typeof(float), typeof(ScaleTransform3D10),
            new PropertyMetadata(0.0f, new PropertyChangedCallback(ScaleY_PropertyChanged)));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="ScaleZ"/>.
        /// </summary>
        public static readonly DependencyProperty ScaleZProperty =
            DependencyProperty.Register("ScaleZ", typeof(float), typeof(ScaleTransform3D10),
            new PropertyMetadata(0.0f, new PropertyChangedCallback(ScaleZ_PropertyChanged)));
        #endregion

        #region DependencyProperties Callbacks
        private static void CenterX_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ((ScaleTransform3D10)sender).OnCenterXChanged(e);
        }

        private static void CenterY_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ((ScaleTransform3D10)sender).OnCenterYChanged(e);
        }

        private static void CenterZ_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ((ScaleTransform3D10)sender).OnCenterZChanged(e);
        }

        private static void ScaleX_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ((ScaleTransform3D10)sender).OnScaleXChanged(e);
        }

        private static void ScaleY_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ((ScaleTransform3D10)sender).OnScaleYChanged(e);
        }

        private static void ScaleZ_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ((ScaleTransform3D10)sender).OnScaleZChanged(e);
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Initialise une nouvelle instance de la classe <see cref="ScaleTransform3D10"/>. 
        /// </summary>
        public ScaleTransform3D10()
        {
            ComputeValue();
        }

        /// <summary>
        /// Initialise une nouvelle instance de la classe <see cref="ScaleTransform3D10"/> à l'aide du <see cref="SlimDX.Vector3"/> spécifié. 
        /// </summary>
        /// <param name="scale"><see cref="SlimDX.Vector3"/> le long duquel la transformation est mise à l'échelle.</param>
        public ScaleTransform3D10(SlimDX.Vector3 scale)
            : this(scale.X, scale.Y, scale.Z)
        { }

        /// <summary>
        /// Initialise une nouvelle instance de la classe <see cref="ScaleTransform3D10"/> avec les <see cref="SlimDX.Vector3"/> spécifiés. 
        /// </summary>
        /// <param name="scale"><see cref="SlimDX.Vector3"/> le long duquel la transformation est mise à l'échelle.</param>
        /// <param name="center">Centre autour duquel la transformation est mise à l'échelle.</param>
        public ScaleTransform3D10(SlimDX.Vector3 scale, SlimDX.Vector3 center)
            : this(scale.X, scale.Y, scale.Z, center.X, center.Y, center.Z)
        { }

        /// <summary>
        /// Initialise une nouvelle instance de la classe <see cref="ScaleTransform3D10"/> à l'aide des facteurs d'échelle spécifiés.
        /// </summary>
        /// <param name="scaleX">Facteur par lequel la valeur X est mise à l'échelle.</param>
        /// <param name="scaleY">Facteur par lequel la valeur Y est mise à l'échelle.</param>
        /// <param name="scaleZ">Facteur par lequel la valeur Z est mise à l'échelle.</param>
        public ScaleTransform3D10(float scaleX, float scaleY, float scaleZ)
        {
            this.ScaleX = scaleX;
            this.ScaleY = scaleY;
            this.ScaleZ = scaleZ;
        }

        /// <summary>
        /// Initialise une nouvelle instance de la classe <see cref="ScaleTransform3D10"/> à l'aide des coordonnées de centre spécifiées et des facteurs d'échelle.
        /// </summary>
        /// <param name="scaleX">Facteur par lequel la valeur X est mise à l'échelle.</param>
        /// <param name="scaleY">Facteur par lequel la valeur Y est mise à l'échelle.</param>
        /// <param name="scaleZ">Facteur par lequel la valeur Z est mise à l'échelle.</param>
        /// <param name="centerX">Coordonnée X du point central de départ de la mise à l'échelle.</param>
        /// <param name="centerY">Coordonnée Y du point central de départ de la mise à l'échelle.</param>
        /// <param name="centerZ">Coordonnée Z du point central de départ de la mise à l'échelle.</param>
        public ScaleTransform3D10(float scaleX, float scaleY, float scaleZ, float centerX, float centerY, float centerZ)
        {
            this.ScaleX = scaleX;
            this.ScaleY = scaleY;
            this.ScaleZ = scaleZ;
            this.CenterX = centerX;
            this.CenterY = centerY;
            this.CenterZ = centerZ;
        }
        #endregion

        #region Data
        private SlimDX.Matrix _value;
        #endregion

        #region Public Properties
        /// <summary>
        /// Obtient ou définit la coordonnée x du point central de la transformation. 
        /// </summary>
        public float CenterX
        {
            get { return (float)GetValue(CenterXProperty); }
            set { SetValue(CenterXProperty, value); }
        }

        /// <summary>
        /// Obtient ou définit la coordonnée y du point central de la transformation. 
        /// </summary>
        public float CenterY
        {
            get { return (float)GetValue(CenterYProperty); }
            set { SetValue(CenterYProperty, value); }
        }

        /// <summary>
        /// Obtient ou définit la coordonnée z du point central de la transformation. 
        /// </summary>
        public float CenterZ
        {
            get { return (float)GetValue(CenterZProperty); }
            set { SetValue(CenterZProperty, value); }
        }

        /// <summary>
        /// Obtient ou définit le facteur d'échelle sur l'axe x. 
        /// </summary>
        public float ScaleX
        {
            get { return (float)GetValue(ScaleXProperty); }
            set { SetValue(ScaleXProperty, value); }
        }

        /// <summary>
        /// Obtient ou définit le facteur d'échelle sur l'axe y. 
        /// </summary>
        public float ScaleY
        {
            get { return (float)GetValue(ScaleYProperty); }
            set { SetValue(ScaleYProperty, value); }
        }

        /// <summary>
        /// Obtient ou définit le facteur d'échelle sur l'axe z. 
        /// </summary>
        public float ScaleZ
        {
            get { return (float)GetValue(ScaleZProperty); }
            set { SetValue(ScaleZProperty, value); }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Crée un clone modifiable de ce <see cref="ScaleTransform3D10"/>, en créant des copies complètes des valeurs de cet objet.
        /// Lors de la copie de propriétés de dépendance, cette méthode copie les références de ressources et les liaisons de données (même si elles ne peuvent plus être résolues), mais pas les animations ni leurs valeurs actuelles.
        /// </summary>
        /// <returns>
        /// Clone modifiable de l'objet actuel.
        /// La valeur de la propriété <see cref="Freezable.IsFrozen"/> de l'objet cloné est false, même si la valeur de la propriété <see cref="Freezable.IsFrozen"/> de la source est true.
        /// </returns>
        public new ScaleTransform3D10 Clone()
        {
            return base.Clone() as ScaleTransform3D10;
        }

        /// <summary>
        /// Crée un clone modifiable de ce <see cref="ScaleTransform3D10"/>, en créant des copies complètes des valeurs actuelles de cet objet.
        /// Contrairement à leurs valeurs actuelles, les références de ressources, les liaisons de données et les animations ne sont pas copiées.
        /// </summary>
        /// <returns>
        /// Clone modifiable de l'objet actuel.
        /// La valeur de la propriété <see cref="Freezable.IsFrozen"/> de l'objet cloné est false, même si la valeur de la propriété <see cref="Freezable.IsFrozen"/> de la source est true.
        /// </returns>
        public new ScaleTransform3D10 CloneCurrentValue()
        {
            return base.CloneCurrentValue() as ScaleTransform3D10;
        }
        #endregion

        #region Private Members
        private void OnCenterXChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ComputeValue();
        }

        private void OnCenterYChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ComputeValue();
        }

        private void OnCenterZChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ComputeValue();
        }

        private void OnScaleXChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ComputeValue();
        }

        private void OnScaleYChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ComputeValue();
        }

        private void OnScaleZChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ComputeValue();
        }

        private void ComputeValue()
        {
            this._value = SlimDX.Matrix.Scaling(ScaleX, ScaleY, ScaleZ);
        }
        #endregion

        #region Transform3D10 Implementation
        /// <summary>
        /// Représentation matricielle de la transformation
        /// </summary>
        public override SlimDX.Matrix Value
        {
            get { return this._value; }
        }
        #endregion

        #region Freezable Implementation
        /// <summary>
        /// Une fois implémentée dans une classe dérivée, initialise une nouvelle instance de la classe dérivée <see cref="Freezable"/>. 
        /// </summary>
        /// <returns>Nouvelle instance.</returns>
        protected override Freezable CreateInstanceCore()
        {
            return new ScaleTransform3D10();
        }
        #endregion
    }
}
